/*
    Author: Christopher Diggins
    License: MIT Licence 1.0
    Website: http://www.cdiggins.com
*/

#ifndef CVML_PATTERN_MATCHER_HPP
#define CVML_PATTERN_MATCHER_HPP

struct Matcher {
	virtual ~Matcher() { };
    virtual bool Matches(Instruction* i) = 0;
	virtual Matcher* Clone() const = 0;
	virtual std::string ToString() const = 0;
};

struct OpMatcher : Matcher 
{
    OpCodeEnum opcode;
	int operand;

    OpMatcher(OpCodeEnum x, int _operand = -1) 
        : opcode(x), operand(_operand) 
    { }

    bool Matches(Instruction* i) {
        return i != NULL && i->opcode == opcode && 
			operand == i->operand;
    }

	Matcher* Clone() const { 
		return new OpMatcher(opcode, operand); 
	}

	std::string ToString() const { 
		std::string r = OpCodeToStr(opcode);
		if (operand >= 0) {
			r += " ";
			r += ToStr(operand);
		}
		return r;
	}
};

struct StringMatcher : Matcher
{
	std::string s;

	StringMatcher(const std::string& _s) 
        : s(_s)
    { }

    bool Matches(Instruction* i) {
        return i != NULL && i->opcode == op_push_string && 
			i->comment == s;
    }

	Matcher* Clone() const { 
		return new StringMatcher(s); 
	}

	std::string ToString() const { 
		return "\"" + s + "\"";
	}
};

struct MatchOutput {
	virtual ~MatchOutput() { };
	virtual void Output(Instruction* i) = 0;
	virtual MatchOutput* Clone() const = 0;
	virtual std::string ToString() const = 0;
};

struct OpOutput : MatchOutput
{
	OpCodeEnum opcode;
	int operand;
	OpOutput(OpCodeEnum op, int _operand = -1) 
		: opcode(op), operand(_operand) 
	{ }
	
	void Output(Instruction* i) {
		i->SetOpCode(opcode, operand); 
	}
	MatchOutput* Clone() const { 
		return new OpOutput(opcode, operand);
	}

	std::string ToString() const {
		std::string r = OpCodeToStr(opcode);
		if (operand >= 0) {
			r += " ";
			r += ToStr(operand);
		}
		return r;
	}
};

struct Rule
{
    std::vector<Matcher*> src;
    std::vector<MatchOutput*> dest;

	Rule() {
	}

	Rule(const Rule& r) {
		for (size_t i = 0; i < r.src.size(); ++i) {
			src.push_back(r.src[i]->Clone());
		}
		for (size_t i = 0; i < r.dest.size(); ++i) {
			dest.push_back(r.dest[i]->Clone());
		}
	}

    ~Rule() {
        DeletePointers(src);
		DeletePointers(dest);
    }

    bool TryRewrite(Instruction* x) {
		// Does it match? 
		Instruction* cur = x;
        for (size_t j = 0; j < src.size(); ++j) {
			if (cur == NULL)
				return false;
            if (!src[j]->Matches(cur))
                return false;
            cur = cur->next;
        }

		// Try to rewrite
		Instruction* cur2 = x;
		for (size_t j = 0; j < dest.size(); ++j) {
			Assert(cur2 != NULL);
			Assert(cur2 != cur, "destination pattern can't be longer than source");
			dest[j]->Output(cur2);
			cur2 = cur2->next;
		}
		while (cur2 != cur) {
			cur2->SetNoop();
			cur2 = cur2->next;
		}
		
		printf("applied rule %s\n", ToString().c_str());

		return true;
    }

	void AddSrc(const Matcher& m) 
	{
		src.push_back(m.Clone()); 
	}

	void AddDest(const MatchOutput& m) 
	{
		dest.push_back(m.Clone());
	}

	std::string ToString() {
		std::string r = "{";
		for (size_t i = 0; i < src.size(); ++i) {
			if (i > 0) {
				r += " ";
			}
			r += src[i]->ToString();
		}
		r += "} => {";
		for (size_t i = 0; i < dest.size(); ++i) {
			if (i > 0) {
				r += " ";
			}
			r += dest[i]->ToString();
		}
		r += "}";
		return r;
	}
};

Rule& operator<<(Rule& r, const Matcher& m)
{
	r.AddSrc(m);
	return r;
}

Rule& operator<<(Rule& r, OpCodeEnum op)
{
	r.AddSrc(OpMatcher(op));
	return r;
}

Rule& operator<<(Rule& r, const char* s)
{
	r.AddSrc(StringMatcher(s));
	return r;
}

Rule& operator>>(Rule& r, const OpOutput& x)
{
	r.AddDest(x);
	return r;
}

Rule& operator>>(Rule& r, OpCodeEnum op)
{
	r.AddDest(OpOutput(op));
	return r;
}

#endif
